package com.qys.training.biz.file.service.impl;

import com.qys.training.base.enumerate.BizCodeEnum;
import com.qys.training.base.enumerate.FontEnum;
import com.qys.training.base.exception.QysException;
import com.qys.training.biz.file.dto.FileFindDTO;
import com.qys.training.biz.file.dto.FileFindRequestDTO;
import com.qys.training.biz.file.entity.FileEntity;
import com.qys.training.biz.file.mapper.FileMapper;
import com.qys.training.biz.file.service.FileService;
import com.qys.training.biz.utils.DateUtil;
import com.qys.training.biz.utils.FileUtil;
import com.qys.training.biz.utils.FontSelector;
import com.qys.training.biz.utils.PDFUtil;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;

/**
 * 文件操作的业务代码
 *
 * @author Winstone
 * @date 2020/8/4 - 2:50 下午
 */
@Service
@Transactional
public class FileServiceImpl implements FileService {


    @Value("${qys.file.storage.path}")
    private String BASE_URI;

    @Value("${qys.file.font.path}")
    private String FONT_SRC;


    @Autowired
    FileMapper fileMapper;

    /**
     * 上传文件，文件保存到指定的目录下，元数据保存到数据库
     *
     * @param file
     * @return id
     */
    public Long uploadFile(MultipartFile file) {
        FileEntity fileEntity = getFileEntity(file);
        fileMapper.insert(fileEntity);
        return fileEntity.getId();
    }

    /**
     * 通过文件ID 对文件进行更改
     *
     * @param id
     * @param file
     * @return id
     */
    public Long updateFile(Long id, MultipartFile file) {
        FileEntity old = fileMapper.selectById(id);
        if (old == null) {
            throw new QysException(BizCodeEnum.FILE_DOES_NOT_EXIST.getCode(),
                    BizCodeEnum.FILE_DOES_NOT_EXIST.getDescription());
        }

        FileEntity newFile = getFileEntity(file);
        newFile.setId(old.getId());
        fileMapper.updateById(newFile);
        File oldFile = new File(old.getPath());
        if (!oldFile.delete()) {
            throw new QysException(BizCodeEnum.FILE_DELETION_FAILED.getCode(),
                    BizCodeEnum.FILE_DELETION_FAILED.getDescription());
        }
        return id;
    }


    /**
     * 通过文件ID 下载文件
     *
     * @param id
     * @return
     */
    public void downloadFile(Long id, HttpServletResponse response) {
        FileEntity fileEntity = fileMapper.selectById(id);
        if (fileEntity == null) {
            throw new QysException(BizCodeEnum.FILE_DOES_NOT_EXIST.getCode(),
                    BizCodeEnum.FILE_DOES_NOT_EXIST.getDescription());
        }

        File file = new File(fileEntity.getPath());
        String name = fileEntity.getFileName();

        doDownload(file, name, response);
    }

    /**
     * 删除文件
     *
     * @param id
     * @return
     */
    public Long deleteFile(Long id) {
        FileEntity fileEntity = fileMapper.selectById(id);
        if (fileEntity == null) {
            throw new QysException(BizCodeEnum.FILE_DOES_NOT_EXIST.getCode(),
                    BizCodeEnum.FILE_DOES_NOT_EXIST.getDescription());
        }
        // 删除元数据
        fileMapper.deleteById(id);

        // 删除文件
        File oldFile = new File(fileEntity.getPath());
        if (!oldFile.delete()) {
            throw new QysException(BizCodeEnum.FILE_DELETION_FAILED.getCode(),
                    BizCodeEnum.FILE_DELETION_FAILED.getDescription());
        }
        return id;

    }

    /**
     * 查询文件元数据
     *
     * @param dto
     * @return
     */
    public List<FileEntity> findFile(FileFindRequestDTO dto) {
        int pageNum = dto.getCurrentPage();
        int pageSize = dto.getPageSize();
        String name = dto.getName();
        Long min = dto.getMin();
        Long max = dto.getMax();

        FileFindDTO findDTO = new FileFindDTO(name, min, max, (pageNum - 1) * pageSize, pageSize);

        List<FileEntity> fileEntityList = fileMapper.findByNameAndSize(findDTO);


        return fileEntityList;
    }

    /**
     * 将文件打包成Zip下载
     *
     * @param ids
     * @param response
     * @return
     */
    public void downloadZipFile(Long[] ids, HttpServletResponse response) {
        String timestamp = DateUtil.getTimestamp();
        // 设置缓存文件夹路径
        String tempPath = BASE_URI + "temp" + File.separator + timestamp + File.separator;
        // 新的Zip包名
        String zipName = DateUtil.getTimestamp() + ".zip";

        // 创建文件列表
        File[] files = new File[ids.length];

        // 将文件列表赋值
        for (int i = 0; i < ids.length; i++) {
            FileEntity fileEntity = fileMapper.selectById(ids[i]);
            // 设置缓存的文件路径
            String copyPath = tempPath + (i + 1) + "." + fileEntity.getFileName();
            try {
                FileUtil.copyFile(fileEntity.getPath(), copyPath);
            } catch (IOException e) {
                e.printStackTrace();
            }
            files[i] = new File(copyPath);
        }

        //设置请求头
        setDownloadHeader(zipName, response);

        // 写入response流
        try {
            FileUtil.zipFiles(files, response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 删除缓存文件和文件夹
        for (File file : files) {
            file.delete();
        }
        // 删除文件夹
        File dir = new File(tempPath);
        dir.delete();
    }

    /**
     * 对PDF文件进行操作
     *
     * @param id
     * @param text
     * @param x
     * @param y
     * @return
     */
    public Long addText(Long id, String text, float x, float y, int pageNum, String font, float fontSize) {
        // 获取文件元数据
        FileEntity fileEntity = fileMapper.selectById(id);

        // 文件原路径
        String src = fileEntity.getPath();

        // 修改后文件缓存路径
        String tempPath = BASE_URI + "temp" + File.separator;
        String disc = tempPath + fileEntity.getFileName();

        // 获取字体文件路径
        String fontPath = FONT_SRC + FontSelector.getFontDescription(font);

        // 设置默认字体
        if (fontSize == 0) {
            fontSize = 18;
        }
        // 设置默认页面
        if (pageNum == 0) {
            pageNum = 1;
        }

        File file = new File(disc);
        try {
            // 添加文字
            PDFUtil.addText(src, disc, text, x, y, pageNum, fontPath, fontSize);

            // 更新文件元数据
            MultipartFile multipartFile = FileUtil.file2multipartFile(file);
            updateFile(id, multipartFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 删除缓存文件
        file.delete();
        return fileEntity.getId();
    }

    /**
     * 获取文件元信息
     *
     * @param file
     * @return FileEntity
     */
    private FileEntity getFileEntity(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        String fileType = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
        String newFileName = UUID.randomUUID().toString().replaceAll("-", "");
        String folderName = DateUtil.getDateString_yyyy_MM_dd();
        String savePath = BASE_URI + folderName + File.separator + newFileName + fileType;

        if (!fileType.equals(".pdf")) {
            throw new QysException(BizCodeEnum.FILE_FORMAT_NOT_SUPPORTED.getCode(), BizCodeEnum.FILE_FORMAT_NOT_SUPPORTED.getDescription());
        }

        try {
            FileUtil.writeFileTo(file.getInputStream(), savePath);
        } catch (IOException e) {
            e.printStackTrace();
        }


        FileEntity fileEntity = new FileEntity();
        fileEntity.setUpdateTime(new Date());
        fileEntity.setCreateTime(new Date());
        fileEntity.setFileName(fileName);
        fileEntity.setFileSize(file.getSize());
        fileEntity.setPath(savePath);
        try {
            fileEntity.setHash(FileUtil.getFileHash(file.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileEntity;
    }

    /**
     * 实际 执行文件下载
     *
     * @param file
     * @param name
     * @param response
     * @return
     */
    private void doDownload(File file, String name, HttpServletResponse response) {
        InputStream inputStream = null;

        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        this.setDownloadHeader(name, response);

        try {
            OutputStream os = response.getOutputStream();
            byte[] temp = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(temp)) != -1) {
                os.write(temp, 0, len);
            }
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置下载的请求头
     *
     * @param name
     * @param response
     */
    public void setDownloadHeader(String name, HttpServletResponse response) {
        response.setContentType("application/force-download");
        // 设置下载后的文件名以及header
        try {
            // 设置文件名
            response.setHeader("Content-disposition", "attachment;fileName=" + URLEncoder.encode(name, "utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}
